package com.mspbots.cw.amqp.receivers;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.mspbots.cw.amqp.BaseReceiver;
import com.mspbots.cw.amqp.model.ExceptionLog;
import com.mspbots.cw.amqp.model.TeamsChannel;
import com.mspbots.cw.amqp.model.TeamsUser;
import com.mspbots.cw.amqp.model.User;
import io.r2dbc.postgresql.codec.Json;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Optional;

/**
 * com.mspbots.cw.amqp.receivers.DataTeamsReceiver
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2020/4/20
 */
@Log4j2
@Component
@RequiredArgsConstructor
public class TeamsUsersReceiver extends BaseReceiver {

    @RabbitListener(queues = "data-teams.groups.channel")
    public void processGroupsChannel(JsonNode content) {
        log.debug("Teams groups channels {}", content);
        TeamsChannel teamsChannel = this.objectMapper.convertValue(content, TeamsChannel.class);
        Optional<TeamsChannel> teamsChannelOpt = this.databaseClient.select()
                .from(TeamsChannel.class)
                .matching(Criteria.where("tenantId").is(content.get("tenantId").asInt())
                        .and("groupId").is(content.get("groupId").asText())
                        .and("channelId").is(content.get("channelId").asText()))
                .fetch().one().blockOptional();
        teamsChannel.setSyncTime(LocalDateTime.now());
        if (teamsChannelOpt.isPresent()) {
            TeamsChannel oldTeamsChannel = teamsChannelOpt.get();
            teamsChannel.setId(oldTeamsChannel.getId());
            this.databaseClient.update().table(TeamsChannel.class).using(teamsChannel)
                    .fetch().rowsUpdated().block();
        } else {
            this.databaseClient.insert().into(TeamsChannel.class).using(teamsChannel).fetch().rowsUpdated().block();
        }

    }

    @RabbitListener(queues = "data-teams.manager")
    public void processUserManager(JsonNode content) {
        try{
            log.debug("Teams user manager {}", content);
            Optional<TeamsUser> teamsUserOpt = this.databaseClient.execute("SELECT *  FROM tenant_teams_user " +
                    "WHERE tenant_id=:tenantId AND teams_user_id =:userId")
                    .as(TeamsUser.class)
                    .bind("tenantId", content.get("tenantId").asInt())
                    .bind("userId", content.get("subUserId").asText())
                    .fetch().one().blockOptional();
            if (teamsUserOpt.isPresent()) {
                TeamsUser teamsUser = teamsUserOpt.get();
                teamsUser.setTeamsManager(content.get("id").asText());
                this.databaseClient.update().table(TeamsUser.class).using(teamsUser)
                        .fetch().rowsUpdated().block();
            }
        }catch (Exception e){
            if (!ObjectUtils.isEmpty(content.get("tenantId"))){
                this.databaseClient.insert().into(ExceptionLog.class).using(
                        ExceptionLog.builder()
                                .tenantId(content.get("tenantId").asInt())
                                .name("data-teams.manager")
                                .status(500)
                                .type("data-teams.manager")
                                .msg(e.getMessage()+","+content.toString() )
                                .build()
                ).fetch().rowsUpdated().block(Duration.ofSeconds(10));
            }
        }
    }

    @RabbitListener(queues = "data-teams.user")
    public void processUser(JsonNode content) {
        try{
            log.debug("Teams user content {}", content);
            JsonNode removedNode = content.get("@removed");
            if (!ObjectUtils.isEmpty(removedNode)) {
                Mono<String> mailOpt = this.databaseClient.execute("SELECT teams_mail FROM tenant_teams_user " +
                        "WHERE tenant_id =:tenantId AND teams_user_id =:userId")
                        .as(String.class)
                        .bind("tenantId", content.get("tenantId").asInt())
                        .bind("userId", content.get("id").asText())
                        .fetch().first();
                Integer result = mailOpt.onErrorResume(err -> {
                    log.error("Find teams mail is error msg: {}", err.getMessage());
                    return Mono.empty();
                }).doOnNext(mail -> log.debug("Update removed user mail {}", mail))
                        .flatMap(mail -> {
                            try {
                                String contentStr = this.objectMapper.writeValueAsString(content);
                                return this.databaseClient
                                        .execute("UPDATE users SET removed=:removed,modified_time=now() WHERE lower(username) = lower(:mail) ")
                                        .as(Integer.class)
                                        .bind("mail", mail)
                                        .bind("removed", Json.of(contentStr))
                                        .fetch().rowsUpdated();
                            } catch (JsonProcessingException e) {
                                log.debug("Json processing error msg: {}", e.getMessage());
                                return Mono.just(0);
                            }
                        }).block();
                log.debug("removed save {}", result);
                return;
            }

            TeamsUser teamsUser = TeamsUser.builder().tenantId(content.get("tenantId").asLong())
                    .teamsUserId(content.get("id").asText())
                    .teamsMail(ObjectUtils.isEmpty(content.get("mail")) ? content.get("userPrincipalName").asText() : content.get("mail").asText())
                    .teamsDisplayName(ObjectUtils.isEmpty(content.get("displayName")) ? null : content.get("displayName").asText())
                    .giveName(ObjectUtils.isEmpty(content.get("givenName")) ? null : content.get("givenName").asText())
                    .jobTitle(ObjectUtils.isEmpty(content.get("jobTitle")) ? null : content.get("jobTitle").asText())
                    .mobilePhone(ObjectUtils.isEmpty(content.get("mobilePhone")) ? null : content.get("mobilePhone").asText())
                    .officeLocation(ObjectUtils.isEmpty(content.get("officeLocation")) ? null : content.get("officeLocation").asText())
                    .preferredLanguage(ObjectUtils.isEmpty(content.get("preferredLanguage")) ? null : content.get("preferredLanguage").asText())
                    .surname(ObjectUtils.isEmpty(content.get("surname")) ? null : content.get("surname").asText())
                    .createDate(LocalDateTime.now())
                    .build();

            Optional<Long> ids = this.databaseClient.execute("SELECT id FROM tenant_teams_user " +
                    "WHERE tenant_id =:tenantId AND teams_user_id =:userId")
                    .as(Long.class)
                    .bind("tenantId", teamsUser.getTenantId())
                    .bind("userId", teamsUser.getTeamsUserId())
                    .fetch().one().blockOptional();

            if (ids.isPresent()) {
                log.debug("Receiver teams user update by id {}", ids.get());
                teamsUser.setId(ids.get());
                teamsUser.setUpdateTime(LocalDateTime.now());
                this.databaseClient.update().table(TeamsUser.class).using(teamsUser)
                        .fetch().rowsUpdated().block();
            } else {
                this.databaseClient.insert().into(TeamsUser.class).using(teamsUser)
                        .fetch().rowsUpdated().block();
            }

            this.autoRegisterUser(teamsUser);
        }catch (Exception e){
            if (!ObjectUtils.isEmpty(content.get("tenantId"))){
                this.databaseClient.insert().into(ExceptionLog.class).using(
                        ExceptionLog.builder()
                                .tenantId(content.get("tenantId").asInt())
                                .name("data-teams.user")
                                .status(500)
                                .type("data-teams.user")
                                .msg(e.getMessage()+","+content.toString() )
                                .build()
                ).fetch().rowsUpdated().block(Duration.ofSeconds(10));
            }
        }
    }

    private void autoRegisterUser(TeamsUser teamsUser) {

        if (ObjectUtils.isEmpty(teamsUser.getTeamsMail())) {
            return;
        }

        User user = User.builder().username(teamsUser.getTeamsMail().toLowerCase())
                .email(teamsUser.getTeamsMail().toLowerCase()).tenantId(teamsUser.getTenantId()).build();
        String[] userName = StringUtils.split(teamsUser.getTeamsDisplayName(), " ");

        if (!ObjectUtils.isEmpty(userName)) {
            user.setFirstName(userName[0].replace(",", ""));
            user.setLastName(userName[1]);
        } else {
            userName = user.getUsername().split("@");
            if (userName[0].contains(".")) {
                log.debug("set username : {}", userName[0]);
                String[] names = userName[0].split("\\.");
                user.setFirstName(userName[0]);
                if (names.length > 0) {
                    log.debug("sync teams names : {}", (Object) names);
                    user.setFirstName(names[0]);
                    user.setLastName(names[1]);
                }
            } else {
                user.setFirstName(userName[0]);
            }
        }
        this.messagingTemplate.convertAndSend("msp.data-teams.sync",
                "data-teams-register-user", user);
    }


}
